【CMake 语法】(12) CMake 宏和函数 您所在的位置:网站首页 clion 查看函数定义 【CMake 语法】(12) CMake 宏和函数

【CMake 语法】(12) CMake 宏和函数

2023-11-10 11:14| 来源: 网络整理| 查看: 265

1. 函数

函数: function, endfunction,支持语法如下:

# function function( [ ...]) # endfunction endfunction([]) 概要 function( [ ...]) endfunction()

定义个名为 的函数,该函数接收名为 … 的参数, 表示函数定义的功能,在调用函数之前不会被执行。

一个函数打开一个新的作用域,请参阅 set(var PARENT_SCOPE)。 https://cmake.org/cmake/help/v3.19/command/set.html

有关函数内部策略的行为,请参阅 cmake_policy()。 https://cmake.org/cmake/help/v3.19/command/cmake_policy.html

调用(Invocation)

函数调用的函数名不区分大小写。

案例,定义一个名为 “foo” 的函数,根据不同大小写的函数名调用。

# 函数定义 function(foo) endfunction() # 函数调用 foo() Foo() FOO() # cmake_language 函数调用方式 cmake_language(CALL foo)

建议函数名全部使用小写。

cmake_language(CALL ...) 既可以调用函数,也可以调用宏。该命令不会引入新的变量或策略范围。

参数(Arguments) 1. 用 形参 引用参数

当函数被调用时,首先通过实参替换形参 ${arg1}, …,然后作为普通命令调用。

案例,定义一个对变量求和的函数。

function(foo x1 x2 x3) math(EXPR value "${x1} + ${x2} + ${x3}") message("x1:${x1}, x2:${x2}, x3:${x3}, sum:${value}") endfunction() foo(10 20 30)

注意:似乎是通过 set(x1 10) 命令将实参值 10 设置到形参变量 x1 中,然后在函数内通过 ${x1} 形式引用参数值。

2. 用 ARGC 变量 和 ARGVn 变量 引用参数

除了引用形参之外,你还可以引用 ARGC 变量 和 ARGVn 变量 来引用参数,ARGC 表示参数数量,以及 ARGV0, ARGV1, ARGV2, … 将具有传入参数的值。这有助于创建带有可选参数的函数。

案例,定义一个函数,输出参数数量和传入的每个参数值

function(foo x1 x2 x3) message("参数数量:${ARGC}, ARGV0:${ARGV0}, ARGV1:${ARGV1}, ARGV2:${ARGV2}") endfunction() foo(10 20 30)

注意: 用 ARGVn 变量引用实参值,与定义的形参 x1, x2, x3 无关,只与传入的实参有关,就算函数定义是 function(main),该函数也照样可以使用,或者说这样还更好。

ARGV 变量保存函数的所有参数的列表。

案例,用 ARGV 变量 引用所有参数

function(foo) message("ARGV:${ARGV}") endfunction() foo(10 20 30 10 10) 3. 用 ARGN 变量引用预期之外的参数

此外,ARGN 变量保存超过形参列表之后的参数。如果实参数量大于形参数量,用 ARGN 变量引用预期之外的参数。

案例,用 ARGN 变量引用预期之外的参数

function(foo x1 x2 x3) message("ARGN:${ARGN}") endfunction() foo(10 20 30 10 10) 4. 形参为变量

之前案例中形参都为值,那么变量取值的语法 ${VAR} 获取的是值。如果形参为变量的话,那么获取的值为变量,要想获取值的话使用 ${${VAR}}。

案例,如果形参为变量的话,参数引用的就是变量

set(a1 10) set(a2 20) set(a3 30) function(foo x1 x2 x3) message("x1:${x1}, x2:${x2}, x3:${x3}") endfunction() foo(a1 a2 a3)

输出结果

x1:a1, x2:a2, x3:a3 2. 宏

宏: macro, endmacro,支持语法如下:

# macro macro( [ ...]) # endmacro endmacro([]) 概要 macro( [ ...]) endmacro()

定义个名为 的宏,该宏接收名为 … 的参数, 表示宏定义的功能,在调用宏之前不会被执行。

有关宏内部策略的行为,请参阅 cmake_policy()。 https://cmake.org/cmake/help/v3.19/command/cmake_policy.html

调用(Invocation)

宏调用名也不区分大小写。

与函数的案例一样,定义一个名为 “foo” 的宏,调用时也可以用 “foo”, “Foo”, “FOO”,也可以使用 cmake_language(CALL ...) 子命令调用宏。

宏也建议,宏名全部使用小写。

参数(Arguments)

调用宏时的参数定义和使用,也与函数一样。

用 形参 引用参数用 ARGC 变量 和 ARGVn 变量 引用参数,用 ARGV 变量 引用所有参数用 ARGN 变量引用预期之外的参数 3. 函数与宏的区别

macro() 与 function() 非常相似。尽管如此,还有一些重要的区别。

在函数中,ARGN, ARGC, ARGV and ARGV0, ARGV1, … 是 CMake 意义上的变量。在宏中,它们不是,它们是字符串替换,就像 C 预处理器 对 宏 所做的一样。这会产生许多后果,如下面的 “参数警告” 部分所述。

宏和函数之间的另一个区别是控制流。函数的执行 通过将控制权从调用语句转移到函数体。宏的执行 就像将宏主题粘贴到调用语句的位置一样。这导致宏体中的 return() 不仅终止宏的执行;相反,控制权是从宏调用的范围内返回的。为了避免混淆,建议在宏中完全避免 return()。

案例,在函数仅仅用 return 将控制权返回给函数的调用者

function(select option) if(${option} EQUAL 1) message("选择选项1") return() elseif(${option} EQUAL 2) message("选择选项2") return() elseif(${option} EQUAL 3) message("选择选项3") return() else() message("未知选项") endif() message("继续执行") endfunction() select(1) select(2) select(0)

案例,如果函数返回返回值,并不是使用 return,而是通过参数

function(foo x1 x2 x3 out_var) math(EXPR value "${x1} + ${x2} + ${x3}") message("x1:${x1}, x2:${x2}, x3:${x3}") set(${out_var} ${value} PARENT_SCOPE) endfunction() foo(10 20 30 RET) message("sum:${RET}")

注意,形参原本的作用域在函数内,只能在函数内使用,但是通过 set(... PARENT_SCOPE) 将变量的作用域设置为当前作用域之外,你可以使用父作用域,也就是作用于函数之外。

注意,如果在函数内部通过 PARENT_SCOPE 设置变量,该变量可以在函数之外使用。

例如,在函数内部设置变量 out_var 的值为 100,在函数外部输出也为 100。

function(foo) set(out_var 100 PARENT_SCOPE) endfunction() foo() message("out_var:${out_var}")

与函数不同,CMAKE_CURRENT_FUNCTION, CMAKE_CURRENT_FUNCTION_LIST_DIR, CMAKE_CURRENT_FUNCTION_LIST_FILE, CMAKE_CURRENT_FUNCTION_LIST_LINE 变量,不是为宏设置的。

4. 参数警告(Argument Caveats)

宏与函数在调用时,参数处理方面的不同。

由于 ARGN, ARGC, ARGV, ARGV0 等,不是变量,因此你无法使用,诸如以下语句:

if(ARGV1) # ARGV1 is not a variable if(DEFINED ARGV2) # ARGV2 is not a variable if(ARGC GREATER 2) # ARGC is not a variable foreach(loop_var IN LISTS ARGN) # ARGN is not a variable

在第一种情况下,你可以使用 if(${ARGV1})。 在第二种和第三种情况下,检查是否将可选变量传递给宏的正确方法是使用 if(${ARGC} GREATER 2)。 在最后一种情况下,你可以使用 foreach(loop_var ${ARGN}) 如果需要包含它们,可以使用

set(list_var "${ARGN}") foreach(loop_var IN LISTS list_var)

在这里插入图片描述

注意:如果在调用宏的范围内有一个同名的变量,则使用未引用的名称,将使用现有变量而不是参数。例如:

macro(bar) foreach(arg IN LISTS ARGN) endforeach() endmacro() function(foo) bar(x y z) endfunction() foo(a b c)

将遍历 a;b;c,而不是预期那样遍历 x;y;z。原因是,bar 会在 foo 中展开,所以 ARGN 称为 foo 中的变量。

5. 用 cmake_parse_arguments 解析参数

我们一般用 cmake_parse_arguments 命令来解析函数或宏的参数。用 cmake_parse_arguments 命令,可以解析复杂的参数形式。

cmake_parse_arguments 有两种形式,语法如下:

cmake_parse_arguments( ...) cmake_parse_arguments(PARSE_ARGV )

我们以 cmake_parse_arguments 的第一种形式为例。

在设计时,有三种参数, 第一种参数是 表示可选关键词列表,如果传入参数包含此变量名,则为 TRUE,反之为 FALSE。 第二种参数是 表示单值关键词列表,每个关键词仅对应一个值。 第三种参数是 表示多值关键词列表,每个关键词可对应多个值。

要解析的参数 ...,我们一般传入为 ${ARGN} 即可,一般定义的函数或宏是无参的,除非第一个参数不是关键词,那么有多少非关键词变量,定义多少形参。

我们将参数 ${ARGN} 根据 , , 规则进行解析,解析出来的新变量名根据 前缀,按照 prefix_参数名 的形式。

可选参数,我们用 if 语句来实现。

案例1,我们想定义一个类似 install 命令,用于安装目标到指定的目录。目前案例仅仅是分析传参,下一个案例是功能的实现框架。

要实现的命令形式,如下:

my_install(TARGETS ... [LIBRARY|RUNTIME|OBJECTS] [CONFIGURATIONS [Debug|Release|...]] DESTINATION [COMPONENT ] [RENAME ] [OPTIONAL] )

我们知道 TARGETS 为多值关键词,LIBRARY,RUNTIME,OBJECTS 为可选关键词,CONFIGURATIONS,DESTINATION,COMPONENT,RENAME 为单值关键词,OPTIONAL 为可选关键词。

代码如下:

macro(my_install) set(options LIBRARY RUNTIME OBJECTS OPTIONAL) set(oneValueArgs DESTINATION COMPONENT RENAME) set(multiValueArgs TARGETS CONFIGURATIONS) cmake_parse_arguments(MY_INSTALL "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) # 多值关键词 message("MY_INSTALL_TARGETS: ${MY_INSTALL_TARGETS}") # 可选关键词 message("MY_INSTALL_LIBRARY: ${MY_INSTALL_LIBRARY}") message("MY_INSTALL_RUNTIME: ${MY_INSTALL_RUNTIME}") message("MY_INSTALL_OBJECTS: ${MY_INSTALL_OBJECTS}") # 多值关键词 message("MY_INSTALL_CONFIGURATIONS: ${MY_INSTALL_CONFIGURATIONS}") # 单值关键词 message("MY_INSTALL_DESTINATION: ${MY_INSTALL_DESTINATION}") message("MY_INSTALL_COMPONENT: ${MY_INSTALL_COMPONENT}") message("MY_INSTALL_RENAME: ${MY_INSTALL_RENAME}") # 可选关键词 message("MY_INSTALL_OPTIONAL: ${MY_INSTALL_OPTIONAL}") # 特殊变量 # 未解析的参数 message("MY_INSTALL_UNPARSED_ARGUMENTS: ${MY_INSTALL_UNPARSED_ARGUMENTS}") # 关键字缺失值的参数 message("MY_INSTALL_KEYWORDS_MISSING_VALUES: ${MY_INSTALL_KEYWORDS_MISSING_VALUES}") endmacro()

my_install() 被调用:

my_install(TARGETS foo bar LIBRARY CONFIGURATIONS DESTINATION bin OPTIONAL blub)

在 cmake_parse_arguments 调用之后,将设置以下变量

MY_INSTALL_TARGETS: foo;bar MY_INSTALL_LIBRARY: TRUE MY_INSTALL_RUNTIME: FALSE MY_INSTALL_OBJECTS: FALSE MY_INSTALL_CONFIGURATIONS: MY_INSTALL_DESTINATION: bin MY_INSTALL_COMPONENT: MY_INSTALL_RENAME: MY_INSTALL_OPTIONAL: TRUE MY_INSTALL_UNPARSED_ARGUMENTS: blub MY_INSTALL_KEYWORDS_MISSING_VALUES: CONFIGURATIONS

TARGETS 为多值关键词,传入值为 foo;bar。 LIBRARY,RUNTIME,OBJECTS 为可选关键词,因为仅传入 LIBRARY,LIBRARY 被设置为 TRUE,其他都设置为 FALSE。 CONFIGURATIONS 仅传入关键字,并未传入值,所以值也为空。 COMPONENT,RENAME 为单值关键词,未被传入,所以值为空。 DESTINATION 为单值关键词,传入值为 bin。 OPTIONAL 为可选关键词,被设置为 TRUE。

有两种特殊的变量, 第一个 _UNPARSED_ARGUMENTS 表示未解析的参数,表示传入的参数并不符合 cmake_parse_arguments 命令解析规则,所以有 blub。 第二个 _KEYWORDS_MISSING_VALUES 表示关键字缺失值的参数,表示单值关键词或多值关键词,仅传入关键字,并未传入值,所以有 CONFIGURATIONS。

案例2,对案例1,增加功能的实现框架。

实现 [LIBRARY|RUNTIME|OBJECTS],可选值,或者三个选一个。

macro(my_install) ... # 实现 [LIBRARY|RUNTIME|OBJECTS],可选值,或者三个选一个。 set(nBool 0) if(MY_INSTALL_LIBRARY) math(EXPR nBool "${nBool} + 1") endif() if(MY_INSTALL_RUNTIME) math(EXPR nBool "${nBool} + 1") endif() if(MY_INSTALL_OBJECTS) math(EXPR nBool "${nBool} + 1") endif() if(${nBool} EQUAL 1) if(${MY_INSTALL_LIBRARY}) message("TODO LIBRARY...") elseif(${MY_INSTALL_RUNTIME}) message("TODO RUNTIME...") elseif(${MY_INSTALL_OBJECTS}) message("TODO OBJECTS...") endif() elseif(nBool EQUAL 0) message("[LIBRARY|RUNTIME|OBJECTS] NOTHING...") else() message("参数错误: [LIBRARY|RUNTIME|OBJECTS]") endif() message("MY_INSTALL_LIBRARY: ${MY_INSTALL_LIBRARY}") message("MY_INSTALL_RUNTIME: ${MY_INSTALL_RUNTIME}") message("MY_INSTALL_OBJECTS: ${MY_INSTALL_OBJECTS}") endmacro()

调用形式

my_install() my_install(LIBRARY) my_install(OBJECTS) my_install(LIBRARY RUNTIME) my_install(OBJECTS RUNTIME) my_install(LIBRARY OBJECTS RUNTIME)

实现 [CONFIGURATIONS [Debug|Release|MinSizeRel|RelWithDebInfo]] 参数为多值关键词,但是值为某些固定选项。

macro(my_install) ... # 实现 `[CONFIGURATIONS [Debug|Release|MinSizeRel|RelWithDebInfo]]` 参数为多值关键词,但是值为某些固定选项。 message("MY_INSTALL_CONFIGURATIONS: ${MY_INSTALL_CONFIGURATIONS}") if(MY_INSTALL_CONFIGURATIONS) foreach(loop_var IN LISTS MY_INSTALL_CONFIGURATIONS) string(STRIP ${loop_var} loop_var) string(TOUPPER ${loop_var} loop_var) #message("loop_var: ${loop_var}") if(${loop_var} STREQUAL "DEBUG") message("TODO DEBUG...") elseif(${loop_var} STREQUAL "RELEASE") message("TODO RELEASE...") elseif(${loop_var} STREQUAL "MINSIZEREL") message("TODO MINSIZEREL...") elseif(${loop_var} STREQUAL "RELWITHDEBINFO") message("TODO RELWITHDEBINFO...") else() message("参数错误: [CONFIGURATIONS [Debug|Release|MinSizeRel|RelWithDebInfo]]") endif() endforeach() endif() endmacro()

调用形式

my_install() my_install(CONFIGURATIONS Debug) my_install(CONFIGURATIONS Release) my_install(CONFIGURATIONS Debug Release) my_install(CONFIGURATIONS Debug Release RelWithDebInfo) my_install(CONFIGURATIONS Debug Release MinSizeRel RelWithDebInfo SizeRel) 我们有时会用 foreach 语句 来处理参数。 function(foo STATUS) message("foo(STATUS ...)") set(_message "STATUS:${STATUS}") list(LENGTH ARGN _message_len) if(${_message_len} EQUAL 1) string(APPEND _message ", :${message}") else(${_message_len} GREATER 2) set(i 0) foreach(loop_var IN LISTS ARGN) string(APPEND _message ", :${loop_var}") math(EXPR i "${i} + 1") endforeach() endif() message("${_message}") endfunction()

调用形式

foo(STATUS "Hello World") foo(STATUS "Hello" " World" "!!!") cmake_parse_arguments 的第二种形式

cmake_parse_arguments 的第二种形式,语法如下:

cmake_parse_arguments(PARSE_ARGV )

PARSE_ARGV 仅用于函数。在这种情况下,解析的参数来自调用函数的 ARGVn。解析从第 个参数开始。

定义一个类似 add_executable 命令,要实现的命令形式,如下:

my_add_executable( [WIN32] [MACOSX_BUNDLE] [EXCLUDE_FROM_ALL] [source1] [source2 ...] )

代码如下:

function(my_add_executable name) set(options WIN32 MACOSX_BUNDLE EXCLUDE_FROM_ALL) set(oneValueArgs) set(multiValueArgs) cmake_parse_arguments(PARSE_ARGV 1 MY "${options}" "${oneValueArgs}" "${multiValueArgs}") message("name: ${name}") message("MY_WIN32: ${MY_WIN32}") message("MY_MACOSX_BUNDLE: ${MY_MACOSX_BUNDLE}") message("MY_EXCLUDE_FROM_ALL: ${MY_EXCLUDE_FROM_ALL}") # 未解析的参数 message("MY_UNPARSED_ARGUMENTS: ${MY_UNPARSED_ARGUMENTS}") # 关键字缺失值的参数 message("MY_KEYWORDS_MISSING_VALUES: ${MY_KEYWORDS_MISSING_VALUES}") endfunction()

调用形式

my_add_executable(myprog WIN32 main1.c main2.c main3.c)

在 cmake_parse_arguments 调用之后,将设置以下变量

name: myprog MY_WIN32: TRUE MY_MACOSX_BUNDLE: FALSE MY_EXCLUDE_FROM_ALL: FALSE MY_UNPARSED_ARGUMENTS: main1.c;main2.c;main3.c MY_KEYWORDS_MISSING_VALUES:

注意,函数第一个参数,可以通过 形参获取 或者通过 ARGV0 引用。最后的不定参数,可以通过特殊变量 MY_UNPARSED_ARGUMENTS 获取,并通过 foreach 语句 解析。

6. 通过特殊变量,查看函数执行情况

我们可以在函数定义中,使用特殊变量,用于查看函数执行的情况。CMAKE_CURRENT_FUNCTION 表示函数名,CMAKE_CURRENT_FUNCTION_LIST_DIR 表示函数所在 CMakeLists.txt 文件目录,CMAKE_CURRENT_FUNCTION_LIST_FILE 表示函数所在 CMakeLists.txt 文件路径CMAKE_CURRENT_FUNCTION_LIST_LINE 表示函数执行所在行数。

Project/src/CMakeLists.txt

cmake_minimum_required(VERSION 3.9.0) project(proj) add_subdirectory(bar) function(foo) message("CMAKE_CURRENT_FUNCTION ${CMAKE_CURRENT_FUNCTION}") message("CMAKE_CURRENT_FUNCTION_LIST_DIR ${CMAKE_CURRENT_FUNCTION_LIST_DIR}") message("CMAKE_CURRENT_FUNCTION_LIST_FILE ${CMAKE_CURRENT_FUNCTION_LIST_FILE}") message("CMAKE_CURRENT_FUNCTION_LIST_LINE ${CMAKE_CURRENT_FUNCTION_LIST_LINE}") endfunction() foo() bar()

Project/src/bar/CMakeLists.txt

function(bar) message("CMAKE_CURRENT_FUNCTION ${CMAKE_CURRENT_FUNCTION}") message("CMAKE_CURRENT_FUNCTION_LIST_DIR ${CMAKE_CURRENT_FUNCTION_LIST_DIR}") message("CMAKE_CURRENT_FUNCTION_LIST_FILE ${CMAKE_CURRENT_FUNCTION_LIST_FILE}") message("CMAKE_CURRENT_FUNCTION_LIST_LINE ${CMAKE_CURRENT_FUNCTION_LIST_LINE}") endfunction()

输出结果为

CMAKE_CURRENT_FUNCTION foo CMAKE_CURRENT_FUNCTION_LIST_DIR Project/src CMAKE_CURRENT_FUNCTION_LIST_FILE Project/src/CMakeLists.txt CMAKE_CURRENT_FUNCTION_LIST_LINE 7 CMAKE_CURRENT_FUNCTION bar CMAKE_CURRENT_FUNCTION_LIST_DIR Project/src/bar CMAKE_CURRENT_FUNCTION_LIST_FILE Project/src/bar/CMakeLists.txt CMAKE_CURRENT_FUNCTION_LIST_LINE 1


【本文地址】

公司简介

联系我们

今日新闻

    推荐新闻

    专题文章
      CopyRight 2018-2019 实验室设备网 版权所有